﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Threading;
using System.Reflection;
using Wyse.Server.Logging;
using Wyse.Server.Config;
using Wyse.Server.Common;
using Wyse.Server.Base;
using System.IO;
using System.Diagnostics;
using Wyse.Server.Game;

namespace Wyse.Server
{
    /// <summary>
    /// Main class of the server
    /// </summary>
    public sealed class ServerRoot : ServerBase
    {
        /// <summary>
        /// Default config file name
        /// </summary>
        public const String ConfigFileName = "config.properties";
        /// <summary>
        /// Default logfile name
        /// </summary>
        public const String LogFileName = "wyse.server.log";

        /// <summary>
        /// Ctor
        /// </summary>
        public ServerRoot(String[] args)
        {
            if (sInstance != null)
                throw new InvalidOperationException();
            sInstance = this;

            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

            // log messages to the console
            Log.AddListener(new ConsoleLogListener());

            // get the root directory
            mRootDirectory = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);

            ProcessCmdLine(args);

            // init the server root directory
            if (mHomeDirectory == null)
            {
                String appData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);

                mHomeDirectory = Path.Combine(appData, "Wyse\\Server");
                try
                {
                    if (!Directory.Exists(mHomeDirectory))
                        Directory.CreateDirectory(mHomeDirectory);
                }
                catch
                {
                    mHomeDirectory = Directory.GetCurrentDirectory();
                }
            }
            else
                mHomeDirectory = Path.GetFullPath(mHomeDirectory);

            // init the logfile
            Log.AddListener(new FileLogListener(Path.Combine(mHomeDirectory, LogFileName)));

            Log.Print(LogLevel.Info, "Server home: {0}", mHomeDirectory);
            Log.Print(LogLevel.Info, "Server root: {0}", mRootDirectory);

            CheckSystem();

            Initialize();
        }

        /// <summary>
        /// Gets the server root's instance
        /// </summary>
        public static ServerRoot Instance
        {
            get { return sInstance; }
        }

        /// <summary>
        /// gets the server's root directory
        /// </summary>
        public String RootDirectory
        {
            get { return mRootDirectory; }
        }

        /// <summary>
        /// Catches all unhandled exceptions
        /// </summary>
        void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            if (e.ExceptionObject is Exception)
                Log.PrintFullException(LogLevel.Fatal, "Unhandled exception:", (Exception)e.ExceptionObject);
            else
                Log.Print(LogLevel.Fatal, "Unhandled exception: {0}", e.ExceptionObject);
        }

        /// <summary>
        /// Processes the command line arguments
        /// </summary>
        /// <param name="args"></param>
        private void ProcessCmdLine(String[] args)
        {
            foreach (var arg in args)
            {
                if (arg[0] == '/')
                {
                    // switches

                    if (arg.StartsWith("/home="))
                    {
                        int p = arg.IndexOf('=');
                        String homeDir = arg.Substring(p + 1);

                        if (Directory.Exists(homeDir))
                            mHomeDirectory = homeDir;
                        else
                            Log.Print(LogLevel.Error, "Directory {0} not exists", homeDir);
                    }
                    else if (arg.StartsWith("/root="))
                    {
                        int p = arg.IndexOf('=');
                        String rootDir = arg.Substring(p + 1);

                        if (Directory.Exists(rootDir))
                            mRootDirectory = rootDir;
                        else
                            Log.Print(LogLevel.Error, "Directory {0} not exists", rootDir);
                    }
                }
                else
                {
                    int p = arg.IndexOf('=');
                    if (p > 0)
                    {
                        String key = arg.Substring(0, p);
                        String value = arg.Substring(p + 1);

                        if (mConfigOverride == null)
                            mConfigOverride = new PropertyFile();
                        mConfigOverride.AddValue(key, value);
                    }
                }
            }
        }

        /// <summary>
        /// Performs the initialization
        /// </summary>
        private void Initialize()
        {
            Log.Print(LogLevel.Info, "WYSE Server {0} initializing", GetVersion());

            // check if the config file exists
            String cfgFile = Path.Combine(mHomeDirectory, ConfigFileName);
            if (!File.Exists(cfgFile))
            {
                String globalCfgFile = Path.Combine(mRootDirectory, ConfigFileName);
                if (File.Exists(globalCfgFile))
                    File.Copy(globalCfgFile, cfgFile);
            }

            ConfigManager.Load(cfgFile, mConfigOverride);

            // access the initial configuration
            ConfigManager.AccessConfig(ca =>
            {
            });

            // initialize the modules

            mGameModule = new GameServerModule();
            AddModule(mGameModule);
        }

        /// <summary>
        /// Performs cleanum
        /// </summary>
        public void Shutdown()
        {
            Log.Print(LogLevel.Info, "Shutting down");
        }

        /// <summary>
        /// Starts all modules
        /// </summary>
        public override void Start()
        {
            Console.CancelKeyPress += new ConsoleCancelEventHandler(Console_CancelKeyPress);

            base.Start();

            Log.Print(LogLevel.Info, "Stopped");
        }

        /// <summary>
        /// Handle Ctrl^C
        /// </summary>
        void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
        {
            e.Cancel = true;
            Log.Print(LogLevel.Info, "Killed");

            try
            {
                Stop();
            }
            catch (Exception re)
            {
                Log.Print(LogLevel.Info,re.ToString());
            }
        }

        /// <summary>
        /// Checks if the operationg system is supported
        /// </summary>
        private void CheckSystem()
        {
            Log.Print(LogLevel.Info, "System check done");
        }

        /// <summary>
        /// Returns the server version as a string
        /// </summary>
        /// <returns></returns>
        public static String GetVersion()
        {
            Assembly asm = typeof(ServerRoot).Assembly;

            return asm.GetName().Version.ToString();
        }

        private GameServerModule mGameModule;

        private String mRootDirectory;
        private String mHomeDirectory;

        private PropertyFile mConfigOverride;

        private static ServerRoot sInstance;
    }
}
